﻿using System;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace MazeGeneration
{
    /// <summary>
    /// A single node in a the maze. Holds references to all the surrounding nodes
    /// as well.
    /// </summary>
    class MazeNode
    {
        public enum WallIndicies
        {
            LEFT_INDEX = 0,
            TOP_INDEX,
            RIGHT_INDEX,
            BOTTOM_INDEX,
            NUM_INDEX,
        };

        /// <summary>
        /// Enums for the different wall directions.
        /// </summary>
        public enum WallTypes
        {
            EMPTY = 0,

            LEFT_FLAG = 1 << WallIndicies.LEFT_INDEX,
            TOP_FLAG = 1 << WallIndicies.TOP_INDEX,
            RIGHT_FLAG = 1 << WallIndicies.RIGHT_INDEX,
            BOTTOM_FLAG = 1 << WallIndicies.BOTTOM_INDEX,
        };

        private WallTypes mWalls;

        private MazeNode[] mNieghbours;

        private Single mLineWidth;

        private Int32 mNodeSize;

        private Int32 mHalfNodeSize;

        private Point mPosition;

        private Boolean mVisited;

        private Pen mPen;

        public MazeNode(Point pos, MazeNode left, MazeNode up, Int32 nodeSize)
        {
            mNieghbours = new MazeNode[(Int32)WallIndicies.NUM_INDEX];

            if (null != left)
            {
                pLeftNode = left;
                left.pRightNode = this;
            }
            if (null != up)
            {
                pTopNode = up;
                up.pBottomNode = this;
            }

            // Start with all walls.
            mWalls = WallTypes.LEFT_FLAG | WallTypes.TOP_FLAG | WallTypes.RIGHT_FLAG | WallTypes.BOTTOM_FLAG;

            mLineWidth = 2;

            mNodeSize = nodeSize;

            mHalfNodeSize = mNodeSize / 2;

            mPosition = pos;

            mVisited = false;

            mPen = new Pen(Color.LawnGreen, 1.0f);
        }

        public void DrawLineToNode(Graphics g, MazeNode node, Color col)
        {
            if (null != node)
            {
                g.DrawLine(
                    new Pen(col, mLineWidth),
                    mPosition,
                    node.pCenter);
            }
        }

        public void Render(Graphics g)
        {
            //DrawLineToNode(g, pLeftNode, Color.Red);
            //DrawLineToNode(g, pTopNode, Color.Green);
            //DrawLineToNode(g, pRightNode, Color.Blue);
            //DrawLineToNode(g, pBottomNode, Color.Yellow);

            if ((mWalls & WallTypes.LEFT_FLAG) == WallTypes.LEFT_FLAG)
            {
                g.DrawLine(
                    mPen,
                    new Point(mPosition.X - mHalfNodeSize, mPosition.Y - mHalfNodeSize),
                    new Point(mPosition.X - mHalfNodeSize, mPosition.Y + mHalfNodeSize));
            }
            if ((mWalls & WallTypes.TOP_FLAG) == WallTypes.TOP_FLAG)
            {
                g.DrawLine(
                    mPen,
                    new Point(mPosition.X - mHalfNodeSize, mPosition.Y - mHalfNodeSize),
                    new Point(mPosition.X + mHalfNodeSize, mPosition.Y - mHalfNodeSize));
            }
            if ((mWalls & WallTypes.RIGHT_FLAG) == WallTypes.RIGHT_FLAG)
            {
                g.DrawLine(
                    mPen,
                    new Point(mPosition.X + mHalfNodeSize, mPosition.Y - mHalfNodeSize),
                    new Point(mPosition.X + mHalfNodeSize, mPosition.Y + mHalfNodeSize));
            }
            if ((mWalls & WallTypes.BOTTOM_FLAG) == WallTypes.BOTTOM_FLAG)
            {
                g.DrawLine(
                    mPen,
                    new Point(mPosition.X - mHalfNodeSize, mPosition.Y + mHalfNodeSize),
                    new Point(mPosition.X + mHalfNodeSize, mPosition.Y + mHalfNodeSize));
            }
        }

        public MazeNode GetNthUnvisitedNieghbour(Int32 index, ref WallIndicies wallIndex)
        {
            Int32 count = 0;

            for (Int32 i = 0; i < 4; i++)
            {
                if (mNieghbours[i] != null)
                {
                    if (mNieghbours[i].pVisited == false)
                    {
                        if (count == index)
                        {
                            wallIndex = (WallIndicies)i;
                            return mNieghbours[i];
                        }

                        count++;
                    }
                }
            }

            return null;
        }

        public void RemoveWall(WallTypes wall)
        {
            mWalls &= ~wall;
        }



        public void RemoveWall(WallIndicies wall)
        {
            RemoveWall((WallTypes)(1 << (Int32)wall));
        }

        public MazeNode pLeftNode
        {
            get
            {
                return mNieghbours[(Int32)WallIndicies.LEFT_INDEX];
            }
            set
            {
                mNieghbours[(Int32)WallIndicies.LEFT_INDEX] = value;
            }
        }

        public MazeNode pRightNode
        {
            get
            {
                return mNieghbours[(Int32)WallIndicies.RIGHT_INDEX];
            }
            set
            {
                mNieghbours[(Int32)WallIndicies.RIGHT_INDEX] = value;
            }
        }

        public MazeNode pTopNode
        {
            get
            {
                return mNieghbours[(Int32)WallIndicies.TOP_INDEX];
            }
            set
            {
                mNieghbours[(Int32)WallIndicies.TOP_INDEX] = value;
            }
        }

        public MazeNode pBottomNode
        {
            get
            {
                return mNieghbours[(Int32)WallIndicies.BOTTOM_INDEX];
            }
            set
            {
                mNieghbours[(Int32)WallIndicies.BOTTOM_INDEX] = value;
            }
        }

        public Point pCenter
        {
            get
            {
                return mPosition;
            }
        }

        public Boolean pVisited
        {
            get
            {
                return mVisited;
            }
            set
            {
                mVisited = value;
            }
        }

        private Boolean SafeCheckIsVisited(MazeNode node)
        {
            return (null != node && false == node.pVisited);
        }

        public Int32 pUnvisitedNieghbours
        {
            get
            {
                Int32 count = 0;

                if (SafeCheckIsVisited(pLeftNode))
                {
                    count++;
                }
                if (SafeCheckIsVisited(pTopNode))
                {
                    count++;
                }
                if (SafeCheckIsVisited(pRightNode))
                {
                    count++;
                }
                if (SafeCheckIsVisited(pBottomNode))
                {
                    count++;
                }

                return count;
            }
        }
    }
}
